package com.irs.irsforum.utils;

import com.irs.irsforum.entity.Behavior;
import com.irs.irsforum.entity.IndexPage;
import com.irs.irsforum.entity.IndexPost;
import com.irs.irsforum.entity.Record;
import com.irs.irsforum.entity.Resources;
import com.irs.irsforum.entity.User;
import com.irs.irsforum.mapper.BehaviorMapper;
import com.irs.irsforum.mapper.PostMapper;
import com.irs.irsforum.mapper.RecordMapper;
import com.irs.irsforum.mapper.UserMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Component
public class RecommendAlgorithm {
    private static final Logger logger =
            LoggerFactory.getLogger(RecommendAlgorithm.class);

    private static final Integer PROBLEMTYPE = 1;
    private static final Integer SUBJECTTYPE = 2;

    private static UserMapper userMapper;
    private static RecordMapper recordMapper;
    private static BehaviorMapper behaviorMapper;
    private static PostMapper postMapper;

    /**
     * 消息推荐算法实现
     * 正确率计算 ： record:   1/(1+inCorrects)  ，正确率相同比较正确时间大小，越晚提交优先级越高（意味着更熟悉）
     *
     * @param problemId 发帖选择的problemId
     * @param subjectId 发帖选择的subjectId
     * @param userId    发帖的用户Id
     */
    public static List<Long> messageRecommend(Long problemId, Long subjectId, Long userId) {
        //Map<Long, Double> correctRank = new LinkedHashMap<>();
        List<Long> studentList = new ArrayList<>();
        User user = userMapper.getUserById(userId);
        /*用于过滤自己*/
        Integer flag = 0;
        Integer logLen = 0;
        if (user != null) {
            if (Resources.getStudentRole().equals(user.getRole())) {
                studentList.add(user.getOriginId());
                logLen++;
                flag = 1;
            }
            Record record = new Record();
            record.setStudentList(studentList);
            record.setProblemId(problemId);
            /*PLAN 1 找到正确率前五的人*/
            if (studentList.size() < Resources.getMessageRecommendNumber() + flag) {
                logger.info("-----进行发帖推荐PLAN1:找到正确率前五的人-----");
                List<Long> students = recordMapper.getInCorrectsByProblem(record);
                List<Long> users = userMapper.getUserListByOriginId(students);
                setList(users, studentList, flag);
                logger.info("通过PLAN1找到的学生为: ={}", studentList.subList(logLen, studentList.size()).toString());
                logLen = studentList.size();
            }
            /*PLAN2 找到该题所属subject中正确率高的5名同学。*/
            if (studentList.size() < Resources.getMessageRecommendNumber() + flag) {
                logger.info("-----进行发帖推荐PLAN2:找到该题所属subject中正确率高的5名同学-----");
                List<Long> students = recordMapper.getInCorrectsBySubject(new Record(subjectId, studentList));
                List<Long> users = userMapper.getUserListByOriginId(students);
                setList(users, studentList, flag);
                logger.info("通过PLAN2找到的学生为: ={}", studentList.subList(logLen, studentList.size()).toString());
                logLen = studentList.size();
            }
            /*PLAN3 找到收藏过、回答过或发过这个subject类别的问题的次数前5-N名同学*/
            if (studentList.size() < Resources.getMessageRecommendNumber() + flag) {
                logger.info("-----进行发帖推荐PLAN3:找到收藏过、回答过或发过这个subject类别的问题的次数前5-N名同学-----");
                List<Long> students = behaviorMapper.getRecommendUser(subjectId, userId);
                setList(students, studentList, flag);
                logger.info("通过PLAN3找到的学生为: ={}", studentList.subList(logLen, studentList.size()).toString());
            }
//        if(studentList.size() < Resources.getMessageRecommendNumber() + flag){
//            logger.info("-----进行发帖推荐PLAN4:找到出题老师-----");
//
//        }
            /*推荐用户列表，移除自己*/
            if (flag == 1) {
                studentList.remove(0);
            }
        }
        return studentList;

    }

    private static void setList(List<Long> users, List<Long> studentList, Integer flag) {
        for (Long user : users) {
            if (studentList.size() == Resources.getMessageRecommendNumber() + flag) {
                break;
            }
            if (!studentList.contains(user)) {
                studentList.add(user);
            }
        }
    }

    /* 首页推荐： 前x条为推荐帖子，根据行为记录的时间以及行为等级进行收集，率先推荐behavior=0并且时间较近的帖子x条，
       不足再推荐behavior=1并且时间较近的帖子，直到凑满x条为止。
      1.通过userId查找behavior中bahaviorType=0的用户，即定义为显性行为的用户所联系problemList与subjectList。
      2.再通过problemList找到x条Post。
      3.若不足x条，则通过协同过滤之problemId的深度显性推荐方法，进行推荐。
      4.若不足x条，则再通过subjectList找到x条Post。
      5.若不足x条，则通过协同过滤之subjectId的深度显性推荐方法，进行推荐。
      6.若不足x条，则在进行隐性行为的查找，方式同上。
   */

    public static Integer indexRecommend(Long userId, List<IndexPost> postList) {
        logger.info("这是首页，并且未发现推荐帖子缓存，开始进行首页帖子推荐---------------------->");
        /*通过显性行为的subject找到未被删除的帖子，并进行推荐*/
        Integer length = setRecommendPost(userId, 0, 0, Resources.getIndexRecommendSize(), postList);
        logger.info("找到显性帖子={}个", length);
        logger.info("分别为 ={}", postList.subList(0, length).toString());
        if (length < Resources.getIndexRecommendSize()) {
            /*通过隐性行为的subject找到未被删除的帖子，并进行推荐*/
            Integer logLen = length;
            Integer recessiveLength = Resources.getIndexRecommendSize() - length;
            length += setRecommendPost(userId, 1, 0, recessiveLength, postList);
            logger.info("找到隐性帖子={}个", length - logLen);
            logger.info("分别为 ={}", postList.subList(logLen, length).toString());
        }
        logger.info("共通过推荐算法找到帖子 ={}个", length);
        return length;
    }

    /**
     * behaviorType = 0 为显性行为
     * behaviorType = 1 为隐性行为
     * 首页推荐算法（1.0）
     * 1.0 仅支持显隐性行为推荐
     * 2.0 支持显隐性行为推荐，并支持深度探索推荐，即显隐性行为的协同过滤。
     */
    private static Integer setRecommendPost(Long userId, Integer behaviorType, Integer start, Integer length, List<IndexPost> postList) {
        /*通过behaviorType和userId找到相关联的problem和subject*/
        logger.info("开始得到该用户行为类型为={}的problem和subject列表-------------->", behaviorType);
        List<IndexPage> rcommendProSubList = behaviorMapper.getRecommendProSubList(new Behavior(userId, behaviorType));
        List<Long> postIdList;
        Integer finalLength = 0;
        if (rcommendProSubList != null && rcommendProSubList.size() > 0) {
            List<IndexPost> recommendIndexPosts;
            logger.info("开始查询该用户行为所属的problemId列表------------->");
            /*得到problem列表*/
            List<Long> recommendProblemId = new ArrayList<>();
            for (IndexPage indexPage : rcommendProSubList) {
                if (indexPage != null && !recommendProblemId.contains(indexPage.getProblemId()) && indexPage.getProblemId() != null) {
                    recommendProblemId.add(indexPage.getProblemId());
                }
            }
            /*通过problem列表找到最新的post*/
            logger.info("开始查询该用户行为所属的problemId所含有的post列表------------->");
            recommendIndexPosts = postMapper.showPostByPage(new IndexPage(start, length, recommendProblemId, null, null, userId));
            recommendIndexPosts.stream().forEach(item -> item.setIsRecommend(1));
            postList.addAll(recommendIndexPosts);
            /*通过查询problemId得到了推荐帖子 length条*/

            logger.info("通过显性/隐性查询problemId得到了推荐帖子 ={} 条", recommendIndexPosts.size());
            logger.info("分别为： postId = {}", postList.subList(postList.size() - recommendIndexPosts.size(), postList.size()).toString());

            finalLength += recommendIndexPosts.size();
            length -= finalLength;
            postIdList = postList.stream().map(IndexPost::getId).collect(Collectors.toList());

            /*协同过滤之problemId的深度推荐方法*/
            if (length > 0) {
                logger.info("开始协同过滤之problemId的深度推荐方法------------->");
                Map<String, Integer> map = collaborativeFiltering(recommendProblemId, postIdList,
                        length, 1, behaviorType, userId, postList, finalLength);
                logger.info("通过协同过滤算法找到={}条", length - map.get("length"));
                logger.info("分别为： postId = {}", postList.subList(finalLength, map.get("finalLength")).toString());
                length = map.get("length");
                finalLength = map.get("finalLength");
            }

            postIdList = postList.stream().map(IndexPost::getId).collect(Collectors.toList());
            List<Long> recommendSubjectId = new ArrayList<>();
            if (length > 0) {
                logger.info("开始查询该用户行为所属的subjectId列表------------->");
                /*得到subject列表*/
                for (IndexPage indexPage : rcommendProSubList) {
                    if (indexPage != null && !recommendSubjectId.contains(indexPage.getSubjectId()) && indexPage.getSubjectId() != null) {
                        recommendSubjectId.add(indexPage.getSubjectId());
                    }
                }
                /*通过subject列表找到最新的post*/
                logger.info("开始查询该用户行为所属的subjectId所含有的post列表------------->");
                recommendIndexPosts = postMapper.showPostByPage(new IndexPage(start, length, null, recommendSubjectId, postIdList, userId));
                recommendIndexPosts.stream().forEach(item -> item.setIsRecommend(1));
                postList.addAll(recommendIndexPosts);
                /*通过查询subjectId得到了推荐帖子 length条*/
                logger.info("通过显性/隐性查询subjectId得到了推荐帖子 ={} 条", recommendIndexPosts.size());
                logger.info("分别为： postId = {}", postList.subList(postList.size() - recommendIndexPosts.size(), postList.size()).toString());
                finalLength += recommendIndexPosts.size();
                length -= recommendIndexPosts.size();
            }

            postIdList = postList.stream().map(IndexPost::getId).collect(Collectors.toList());
            /*协同过滤之subjectId的深度推荐方法*/
            if (length > 0) {
                logger.info("开始协同过滤之subjectId的深度推荐方法------------->");
                Map<String, Integer> map = collaborativeFiltering(recommendSubjectId, postIdList,
                        length, 2, behaviorType, userId, postList, finalLength);
                logger.info("通过协同过滤算法找到={}条", length - map.get("length"));
                logger.info("分别为： postId = {}", postList.subList(finalLength, map.get("finalLength")).toString());
                length = map.get("length");
                finalLength = map.get("finalLength");
            }
            return finalLength;
        }
        return 0;
    }

    /**
     * 协同过滤之subjectId（显性）当前用户    A   B   C
     * subject1               0           0   0
     * subject2              推荐         0   0
     * subject3               0       0   0
     * subject4              推荐     0   0   0
     * 即找到与当前用户行为相同（喜欢的subject相同的用户，找到这些用户一起喜欢的最多的subject，给当前用户进行推荐,若喜欢当前subject的用户小于等于2（包括自己），则不推荐）
     */
    private static Map collaborativeFiltering(List<Long> idList,
                                              List<Long> postIdList,
                                              Integer length,
                                              Integer flag,
                                              Integer behaviorType,
                                              Long userId,
                                              List<IndexPost> postList,
                                              Integer finalLength) {
        Map<String, Integer> map = new HashMap<>(16);
        map.put("length", length);
        map.put("finalLength", finalLength);

        logger.info("协同过滤算法实现");
        if (flag.equals(PROBLEMTYPE)) {
            logger.info("进行协同过滤之behaviorType ={}的根据problemId推荐", behaviorType);
            IndexPage indexPage;
            for (Long id : idList) {
                indexPage = new IndexPage(postIdList, userId, null, id, behaviorType);
                addList(indexPage, behaviorType, length, map, postList);
                if (postList.size() != postIdList.size()) {
                    postIdList = postList.stream().map(e -> e.getId()).collect(Collectors.toList());
                }
            }
            return map;
        } else if (flag.equals(SUBJECTTYPE)) {
            logger.info("进行协同过滤之behaviorType ={}的根据subjectId推荐", behaviorType);
            IndexPage indexPage;
            for (Long id : idList) {
                indexPage = new IndexPage(postIdList, userId, id, null, behaviorType);
                addList(indexPage, behaviorType, length, map, postList);
                if (postList.size() == postIdList.size()) {
                    postIdList = postList.stream().map(e -> e.getId()).collect(Collectors.toList());
                }
            }
            return map;
        }
        return null;
    }

    private static void addList(IndexPage indexPage,
                                Integer behaviorType,
                                Integer length,
                                Map<String, Integer> map,
                                List<IndexPost> postList) {
        List<IndexPost> recommendIndexPosts = managerUserList(indexPage, behaviorType, length);
        if (recommendIndexPosts != null && recommendIndexPosts.size() > 0 && length > 0) {
            map.put("finalLength", map.get("finalLength") + recommendIndexPosts.size());
            map.put("length", map.get("length") - recommendIndexPosts.size());
            recommendIndexPosts.stream().forEach(item -> item.setIsRecommend(2));
            postList.addAll(recommendIndexPosts);
        }
    }

    private static List<IndexPost> managerUserList(IndexPage indexPage, Integer behaviorType, Integer length) {
        List<Long> userIdList = behaviorMapper.getUserByCollaborativeFiltering(indexPage);
        if (userIdList.size() > 0) {
            List<IndexPage> maxPostNum = behaviorMapper.getMaxPostNum(new IndexPage(indexPage.getPostList(), userIdList, behaviorType, length));
            List<Long> postIdList = new ArrayList<>();
            for (IndexPage post : maxPostNum) {
                postIdList.add(post.getPostId());
            }
            if (postIdList.size() > 0){
                return postMapper.getIndexPostByList(new IndexPage(indexPage.getPostList(), postIdList, indexPage.getUserId()));
            }
        }
        return null;
    }

    @Autowired
    public void setUserMapper(UserMapper userMapper) {
        RecommendAlgorithm.userMapper = userMapper;
    }

    @Autowired
    public void setRecordMapper(RecordMapper recordMapper) {
        RecommendAlgorithm.recordMapper = recordMapper;
    }

    @Autowired
    public void setBehaviorMapper(BehaviorMapper behaviorMapper) {
        RecommendAlgorithm.behaviorMapper = behaviorMapper;
    }

    @Autowired
    public void setPostMapper(PostMapper postMapper) {
        RecommendAlgorithm.postMapper = postMapper;
    }
}
